package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsola.common.DateKit;
import org.jsola.common.DateKit8;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.ISocDispatchEmpAddDAO;
import org.jsola.hr.dto.SocDispatchEmpAddAddDTO;
import org.jsola.hr.dto.SocDispatchEmpAddAndItemUpdateDTO;
import org.jsola.hr.dto.SocDispatchEmpAddUpdateDTO;
import org.jsola.hr.dto.SocDispatchItemUpdateDTO;
import org.jsola.hr.dto.excel.SocDispatchEmpAddImportDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.query.SocDispatchEmpAddQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 社保派单员工表_增员
 *
 * @author zhr
 */
@Slf4j
@Service("hrSocDispatchEmpAddServiceImpl")
public class SocDispatchEmpAddServiceImpl implements ISocDispatchEmpAddService {

    @Autowired
    private ISocDispatchEmpAddDAO socDispatchEmpAddDAO;

    @Autowired
    private ISocDispatchService socDispatchService;

    @Autowired
    private ISocProgService socProgService;

    @Autowired
    private ISocDispatchEmpItemService socDispatchEmpItemService;

    @Autowired
    private ISocProgItemService socProgItemService;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    @Autowired
    private IEmpSocDispatchService empSocDispatchService;

    @Autowired
    private IGroupCityService groupCityService;

    @Autowired
    private IChildCompanyService childCompanyService;

    @Autowired
    private IGroupCompanyService groupCompanyService;

    @Autowired
    private ICompanyService companyService;
    private final BigDecimal decimal0 = new BigDecimal(0);

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SocDispatchEmpAddVO save(SocDispatchEmpAddAddDTO socDispatchEmpAddAddDTO, TokenUser tokenUser) {
        // 转DO
        SocDispatchEmpAddDO socDispatchEmpAddDO = socDispatchEmpAddAddDTO.to(SocDispatchEmpAddDO.class);
        // 保存
        socDispatchEmpAddDO = save(socDispatchEmpAddDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return socDispatchEmpAddDO.to(SocDispatchEmpAddVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SocDispatchVO batchSave(String companyId, String socDispatchId, boolean isUpdate, List<SocDispatchEmpAddImportDTO> socDispatchEmpAddImportList, String year, String month, Map<Integer, String> headMap, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(socDispatchEmpAddImportList)) {
            throw new ParamException("没有需要导入的数据,请检查");
        }

        // 新增或修改导入记录
        SocDispatchVO socDispatch = socDispatchService.addOrUpdateSocDispatch(companyId, socDispatchId, isUpdate, true, year, month, tokenUser);

        int sort = 1;
        // 参保方案获取
        for (SocDispatchEmpAddImportDTO socDispatchEmpAddImport : socDispatchEmpAddImportList) {
            SocDispatchEmpAddAddDTO socDispatchEmpAdd = socDispatchEmpAddImport.to(SocDispatchEmpAddAddDTO.class);
            // 导入记录id和默认数据
            socDispatchEmpAdd.setSocDispatchId(socDispatch.getId());
            socDispatchEmpAdd.setYear(year);
            socDispatchEmpAdd.setMonth(month);
            socDispatchEmpAdd.setHasSend(false);
            socDispatchEmpAdd.setHasReceive(false);
            socDispatchEmpAdd.setStatus(0);
            socDispatchEmpAdd.setCompanyId(companyId);
            socDispatchEmpAdd.setSort(sort);
            sort++;

            // 用增员年月的当月第一天
            if (socDispatchEmpAdd.getSocStartDate() != null) {
                socDispatchEmpAdd.setSocStartDate(DateKit8.getFirstDayOfMonth(socDispatchEmpAdd.getSocStartDate()));
            }
            if (socDispatchEmpAdd.getFundStartDate() != null) {
                socDispatchEmpAdd.setFundStartDate(DateKit8.getFirstDayOfMonth(socDispatchEmpAdd.getFundStartDate()));
            }
            CompanyDO companyDO = companyService.selectDOById(companyId, tokenUser.getSiteId());
            if(!ObjectUtils.isEmpty(companyDO)){
                socDispatchEmpAdd.setAssignCompanyId(companyDO.getId());
                socDispatchEmpAdd.setAssignCompanyName(companyDO.getName());
            }
            // 根据方案名称获取方案
            SocProgDO socProg = socProgService
                    .selectSocProgByName(socDispatchEmpAdd.getSocProgName(), companyId, tokenUser.getSiteId());
            boolean noError = Objects.nonNull(socProg);
            if(!ObjectUtils.isEmpty(socProg)){
                socDispatchEmpAdd.setSocProgId(socProg.getId());
                socDispatchEmpAdd.setSocProgName(socProg.getProgName());
                socDispatchEmpAdd.setSocCityId(Long.valueOf(socProg.getSocCity()));
                socDispatchEmpAdd.setSocCityName(socProg.getSocCityName());
            }
            // 保存员工导入记录
            socDispatchEmpAdd.setNoError(noError);
            SocDispatchEmpAddVO socDispatchEmpAddVO = save(socDispatchEmpAdd, tokenUser);
            // 分项信息
            boolean itemNoError = publishSocDispatchEmpAddItem(socDispatch.getId(), socDispatchEmpAddVO.getId(), socProg, headMap, socDispatchEmpAddImport, tokenUser);
            if (itemNoError) {
                // 子项没有错误,统计子项数据,汇总到总的
                sumItemAndSave(socDispatchEmpAddVO.getId(), tokenUser);
            } else if (noError) {
                // 分项有错误信息,主数据没有,修改错误状态
                SocDispatchEmpAddUpdateDTO updateDTO = new SocDispatchEmpAddUpdateDTO();
                updateDTO.setId(socDispatchEmpAddVO.getId());
                updateDTO.setNoError(false);
                update(updateDTO, tokenUser);
            }
        }
        // 导入记录
        return socDispatchService.selectById(socDispatch.getId(), tokenUser.getSiteId());
    }

    /**
     * 汇总分项数据,保存
     *
     * @param id        id
     * @param tokenUser 当前用不
     */
    private void sumItemAndSave(String id, TokenUser tokenUser) {
        SocDispatchEmpAddDO socAdd = new SocDispatchEmpAddDO();
        socAdd.setId(id);

        List<SocDispatchEmpItemDO> itemList = socDispatchEmpItemService.listByEmpDispatchSocId(id, tokenUser.getSiteId());

        //个人社保缴费
        BigDecimal personalSocAmount = decimal0;
        //个人公积金缴费
        BigDecimal personalPfAmount = decimal0;
        //公司社保缴费
        BigDecimal companySocAmount = decimal0;
        //公司公积金缴费
        BigDecimal companyPfAmount = decimal0;

        for (SocDispatchEmpItemDO item : itemList) {
            if (!item.getNoError()) {
                return;
            }
            if (item.getItemType() == 1) {
                // 社保
                personalSocAmount = personalSocAmount.add(item.getAmountPersonal());
                companySocAmount = companySocAmount.add(item.getAmountCompany());
            } else {
                // 公积金
                personalPfAmount = personalPfAmount.add(item.getAmountPersonal());
                companyPfAmount = companyPfAmount.add(item.getAmountCompany());
            }
        }
        //个人总缴费
        BigDecimal personalAmount = personalSocAmount.add(personalPfAmount);
        //公司总缴费
        BigDecimal companyAmount = companySocAmount.add(companyPfAmount);
        //公司总缴费
        BigDecimal allAmount = companyAmount.add(personalAmount);

        // 汇总数据请传递社保参保时间
        socAdd.setPersonalSocAmount(personalSocAmount);
        socAdd.setPersonalPfAmount(personalPfAmount);
        socAdd.setPersonalAmount(personalAmount);
        socAdd.setCompanySocAmount(companySocAmount);
        socAdd.setCompanyPfAmount(companyPfAmount);
        socAdd.setCompanyAmount(companyAmount);
        socAdd.setAllAmount(allAmount);
        // 修改
        socDispatchEmpAddDAO.updateByPrimaryKeySelective(socAdd);
    }

    /**
     * 员工社保详情信息
     *
     * @param socDispatchEmpId        员工添加记录id
     * @param socProg                 参保方案
     * @param headMap                 导入的标题,获取自定义的参保类型使用
     * @param socDispatchEmpAddImport 员工的参保信息
     * @param tokenUser               当前用户
     */
    private boolean publishSocDispatchEmpAddItem(String socDispatchId, String socDispatchEmpId, SocProgDO socProg,
                                                 Map<Integer, String> headMap, SocDispatchEmpAddImportDTO socDispatchEmpAddImport, TokenUser tokenUser) {
        // 自定义表单开始列,从0开始
        int startNumber = 7;
        boolean itemNoError = true;

        // 转object,便于取属性
        JSONObject obj = JSON.parseObject(JSONObject.toJSONString(socDispatchEmpAddImport));

        // 获取参保方案子项map,<子项名称,子项信息>map
        Map<String, SocProgItemDO> itemMap = getSocProgItemMap(socProg, tokenUser.getSiteId());

        List<SocDispatchEmpItemDO> itemAddList = new ArrayList<>();
        for (int i = startNumber; i < headMap.size(); i++) {
            // 保险名称
            String headName = headMap.get(i);

            // 保险基数
            BigDecimal baseValue;
            if (obj.get("base" + (i - startNumber)) == null) {
                baseValue = decimal0;
            } else {
                baseValue = obj.getBigDecimal("base" + (i - startNumber));
            }
            baseValue = baseValue.setScale(2, BigDecimal.ROUND_HALF_UP);

            // 子项
            SocDispatchEmpItemDO itemAddDTO = new SocDispatchEmpItemDO();
            itemAddDTO.setSocDispatchId(socDispatchId);
            itemAddDTO.setEmpDispatchSocId(socDispatchEmpId);
            itemAddDTO.setSocProgId(Objects.nonNull(socProg) ? socProg.getId() : "");

            // 子项名称,参保基数
            itemAddDTO.setItemName(headName);
            itemAddDTO.setCardinalityPerson(baseValue);
            // 参保方案预设的值,没有,设置数据为问题
            boolean noError = true;
            String errorMsg = "";

            // 获取参保子项
            SocProgItemDO socProgItem = itemMap.get(headName);
            // 剔除已使用的,便于处理没有的项
            itemMap.remove(headName);

            if (Objects.nonNull(socProgItem)) {
                if (baseValue.compareTo(socProgItem.getCardinalityMax()) == 1) {
                    // 大于最大范围
                    noError = false;
                    errorMsg = "参保基数大于参保方案参数范围";
                }
                if (baseValue.compareTo(socProgItem.getCardinalityMin()) == -1) {
                    // 小于参保范围
                    noError = false;
                    errorMsg = "参保基数小于参保方案参数范围";
                }
                // 根据设置的参保方案构建员工
                bulidEmpItemBySocProgItem(baseValue, socProg.getOnlySocAmount(), socProgItem, itemAddDTO);
            } else {
                // 没有参保项的构建
                bulidEmpItemNoSocProgItem(headName, itemAddDTO);
                // 没有对应参保项,基数还不是0
                if (baseValue.compareTo(new BigDecimal(0)) != 0) {
                    noError = false;
                    errorMsg = "参保方案中没有对应的参保子项";
                    itemAddDTO.setCardinalityPerson(baseValue);
                }
            }

            // 错误信息
            itemAddDTO.setNoError(noError);
            itemAddDTO.setErrorMsg(errorMsg);
            if (!noError) {
                itemNoError = false;
            }

            itemAddDTO.preInsert(tokenUser.getUserId());
            itemAddDTO.setSiteId(tokenUser.getSiteId());

            itemAddList.add(itemAddDTO);
        }
        // 尚有参保项没有处理,使用默认的设置
        if (itemMap.size() > 0) {
            for (String code : itemMap.keySet()) {
                // 获取参保子项
                SocProgItemDO socProgItem = itemMap.get(code);
                // 根据默认的构建参保基数
                SocDispatchEmpItemDO itemAddDTO = buildItemByDefault(socDispatchId, socDispatchEmpId, socProg.getId(), socProg.getOnlySocAmount(), socProgItem);
                // 添加
                itemAddList.add(itemAddDTO);
            }
        }

        // 批量保存
        socDispatchEmpItemService.batchSaveDoList(itemAddList, tokenUser);

        return itemNoError;
    }

    /**
     * 根据参保方案设置的值计算员工参保数据
     *
     * @param empValue      员工参保基数
     * @param onlySocAmount 仅设置参保金额（0：否，1：是）
     * @param socProgItem   参保方案设置
     * @param empItem       员工参保数据
     */
    private void bulidEmpItemBySocProgItem(BigDecimal empValue, Boolean onlySocAmount, SocProgItemDO socProgItem, SocDispatchEmpItemDO empItem) {

        empItem.setItemType(socProgItem.getItemType());
        empItem.setItemName(socProgItem.getItemName());
        // 仅设置参保金额（0：否，1：是）
        if (onlySocAmount) {
            empItem.setCardinalityDefault(socProgItem.getCardinalityDefaultAmount());
        } else {
            empItem.setCardinalityDefault(socProgItem.getCardinalityDefault());
        }

        empItem.setCardinalityMax(socProgItem.getCardinalityMax());
        empItem.setCardinalityMin(socProgItem.getCardinalityMin());
        // 公司比例,个人比例
        empItem.setPersentCompany(socProgItem.getPersentCompany());
        empItem.setPersentPersonal(socProgItem.getPersentPersonal());

        // 仅设置参保金额（0：否，1：是）
        if (onlySocAmount) {
            empItem.setAmountCompany(socProgItem.getAmountCompany());
            empItem.setAmountPersonal(socProgItem.getAmountPersonal());
        } else {
            // 根据比例计算
            // 公司金额,个人金额
            BigDecimal b100 = new BigDecimal(100);
            BigDecimal amountCompany = empValue.multiply(socProgItem.getPersentCompany()).divide(b100).setScale(2, BigDecimal.ROUND_HALF_UP);
            empItem.setAmountCompany(amountCompany);
            BigDecimal amountPersonal = empValue.multiply(socProgItem.getPersentPersonal()).divide(b100).setScale(2, BigDecimal.ROUND_HALF_UP);
            empItem.setAmountPersonal(amountPersonal);
        }
        // 总缴费金额
        empItem.setAmountAll(empItem.getAmountCompany().add(empItem.getAmountPersonal()));
        empItem.setSort(socProgItem.getSort());
    }

    /**
     * 参保方案中没有参保子项的厚茧
     *
     * @param name    参保名称
     * @param empItem 员工参保数据
     */
    private void bulidEmpItemNoSocProgItem(String name, SocDispatchEmpItemDO empItem) {
        if (name.contains("公积金")) {
            empItem.setItemType(2);
        } else {
            empItem.setItemType(1);
        }
        empItem.setItemName(name);
        empItem.setCardinalityDefault(decimal0);
        empItem.setCardinalityMax(decimal0);
        empItem.setCardinalityMin(decimal0);
        // 公司比例,个人比例
        empItem.setPersentCompany(decimal0);
        empItem.setPersentPersonal(decimal0);
        empItem.setAmountCompany(decimal0);
        empItem.setAmountPersonal(decimal0);
        // 总缴费金额
        empItem.setAmountAll(decimal0);

        empItem.setSort(100);
    }

    /**
     * 根据参保默认子项构建员工参保子项信息
     *
     * @param socDispatchEmpId 员工参保id
     * @param socProgId        参保方案id
     * @param onlySocAmount    仅设置参保金额
     * @param socProgItem      默认参保子项信息
     * @return 员工参保子项信息
     */
    private SocDispatchEmpItemDO buildItemByDefault(String socDispatchId, String socDispatchEmpId, String socProgId, Boolean onlySocAmount, SocProgItemDO socProgItem) {
        SocDispatchEmpItemDO itemAddDTO = new SocDispatchEmpItemDO();
        itemAddDTO.setSocDispatchId(socDispatchId);
        itemAddDTO.setEmpDispatchSocId(socDispatchEmpId);
        itemAddDTO.setSocProgId(socProgId);
        // 员工参数为默认的基数
        itemAddDTO.setCardinalityPerson(socProgItem.getCardinalityDefault());

        // 根据设置的参保方案构建员工
        bulidEmpItemBySocProgItem(socProgItem.getCardinalityDefault(), onlySocAmount, socProgItem, itemAddDTO);

        itemAddDTO.setNoError(true);
        itemAddDTO.setErrorMsg("");
        return itemAddDTO;
    }

    /**
     * 获取参保方案子项Map,<子项名称,子项信息>
     *
     * @param socProg 参保方案
     * @param siteId  站点
     * @return 参保方案Map
     */
    private Map<String, SocProgItemDO> getSocProgItemMap(SocProgDO socProg, String siteId) {
        // <子项名称,子项信息>map
        Map<String, SocProgItemDO> itemMap = new HashMap<>();
        if (Objects.nonNull(socProg)) {
            List<SocProgItemDO> socProgItemList = socProgItemService.selectBySocProgId(socProg.getId(), siteId);
            if (!CollectionUtils.isEmpty(socProgItemList)) {
                itemMap = socProgItemList.parallelStream().collect(
                        Collectors.toMap(
                                SocProgItemDO::getItemName,
                                Function.identity()
                        )
                );
            }
        }
        return itemMap;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(SocDispatchEmpAddUpdateDTO socDispatchEmpAddUpdateDTO, TokenUser tokenUser) {
        //转DO
        SocDispatchEmpAddDO socDispatchEmpAddDO = socDispatchEmpAddUpdateDTO.to(SocDispatchEmpAddDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(socDispatchEmpAddDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... socDispatchEmpAddIds) {
        if (socDispatchEmpAddIds == null || socDispatchEmpAddIds.length == 0) {
            return 0;
        }

        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andIn("id", Arrays.asList(socDispatchEmpAddIds))
                .andEqualTo("valid", true)
                .andEqualTo("hasSend", true)
                .andEqualTo("siteId", tokenUser.getSiteId());

        int hasEndCount = socDispatchEmpAddDAO.selectCountByExample(example);
        if (hasEndCount > 0) {
            throw new ParamException("要删除的派单员工记录中存在已派单的员工,请不要删除");
        }

        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) socDispatchEmpAddIds);
    }


    @Override
    public SocDispatchEmpAddVO selectById(String socDispatchEmpAddId, String siteId) {
        SocDispatchEmpAddDO socDispatchEmpAddDO = selectDOById(socDispatchEmpAddId, siteId);
        if (socDispatchEmpAddDO == null) {
            return null;
        }
        return socDispatchEmpAddDO.to(SocDispatchEmpAddVO.class);
    }

    @Override
    public List<SocDispatchEmpAddListVO> select(SocDispatchEmpAddQuery socDispatchEmpAddQuery, String siteId) {
        List<SocDispatchEmpAddListVO> list = getVoListByDoList(selectDO(socDispatchEmpAddQuery, siteId));
        if (socDispatchEmpAddQuery.getShowItem()) {
            if (!CollectionUtils.isEmpty(list)) {
                list.forEach(
                        socDispatchEmpAdd -> {
                            // 添加分项信息
                            addItemToEmpAdd(socDispatchEmpAdd, siteId);
                        }
                );
            }
        }
        return list;
    }

    /**
     * 根据doList获取voList
     *
     * @param doList doList
     * @return voList
     */
    private List<SocDispatchEmpAddListVO> getVoListByDoList(List<SocDispatchEmpAddDO> doList) {
        if (CollectionUtils.isEmpty(doList)) {
            return doList == null ? null : new ArrayList<>();
        }
        return doList.stream()
                .map(socDispatchEmpAddDO -> socDispatchEmpAddDO.to(SocDispatchEmpAddListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(SocDispatchEmpAddQuery socDispatchEmpAddQuery, String siteId) {
        Example example = buildExample(socDispatchEmpAddQuery, siteId);
        return socDispatchEmpAddDAO.selectCountByExample(example);
    }

    @Override
    public Page<SocDispatchEmpAddListVO> selectPage(SocDispatchEmpAddQuery socDispatchEmpAddQuery, String siteId) {
        Example example = buildExample(socDispatchEmpAddQuery, siteId);
        Page<SocDispatchEmpAddDO> page = socDispatchEmpAddDAO.selectPageByExample(example,
                socDispatchEmpAddQuery.getPageNo(),
                socDispatchEmpAddQuery.getPageSize());
        Page<SocDispatchEmpAddListVO> pageVo = page.to(SocDispatchEmpAddListVO.class);
        if (socDispatchEmpAddQuery.getShowItem()) {
            if (!CollectionUtils.isEmpty(pageVo.getEntities())) {
                pageVo.getEntities().forEach(
                        socDispatchEmpAdd -> {
                            // 添加分项信息
                            addItemToEmpAdd(socDispatchEmpAdd, siteId);
                        }
                );
            }
        }
        return pageVo;
    }

    /**
     * 将参保分项信息添加到导入记录
     *
     * @param socDispatchEmpAdd 导入记录
     * @param siteId            站点id
     */
    private void addItemToEmpAdd(SocDispatchEmpAddListVO socDispatchEmpAdd, String siteId) {
        if (Objects.nonNull(socDispatchEmpAdd)) {
            List<String> itemNameList = socDispatchEmpItemService.listItemNameBySocDispatch(socDispatchEmpAdd.getSocDispatchId(), siteId);
            // 根据所有参保项排序后
            List<SocDispatchEmpItemListVO> itemSortList = new ArrayList<>();

            // 已有的
            List<SocDispatchEmpItemListVO> itemList = socDispatchEmpItemService.listVoByEmpDispatchSocId(socDispatchEmpAdd.getId(), siteId);

            itemNameList.forEach(
                    itemName -> {
                        SocDispatchEmpItemListVO empItem = null;
                        if (!CollectionUtils.isEmpty(itemList)) {
                            empItem = itemList.parallelStream()
                                    .filter(item -> itemName.equals(item.getItemName()))
                                    .findFirst().orElse(null);
                        }
                        if (Objects.isNull(empItem)) {
                            empItem = new SocDispatchEmpItemListVO();
                            empItem.setSocDispatchId(socDispatchEmpAdd.getSocDispatchId());
                            empItem.setEmpDispatchSocId(socDispatchEmpAdd.getId());
                            empItem.setSocProgId(socDispatchEmpAdd.getSocProgId());
                            empItem.setItemType(1);
                            empItem.setItemName(itemName);
                            empItem.setCardinalityPerson(decimal0);
                            empItem.setCardinalityDefault(decimal0);
                            empItem.setCardinalityMin(decimal0);
                            empItem.setCardinalityMax(decimal0);
                            empItem.setPersentCompany(decimal0);
                            empItem.setPersentPersonal(decimal0);
                            empItem.setAmountCompany(decimal0);
                            empItem.setAmountPersonal(decimal0);
                            empItem.setAmountAll(decimal0);
                            empItem.setItemRemark("其他参保补充的");
                            empItem.setNoError(true);
                            empItem.setSort(100);
                            empItem.setIsNowItem(false);
                        }
                        itemSortList.add(empItem);
                    }
            );
            socDispatchEmpAdd.setSocDispatchEmpItemList(itemSortList);
        }
    }

    @Override
    public SocDispatchEmpAddDO selectDOById(String socDispatchEmpAddId, String siteId) {
        return listById(socDispatchEmpAddId, siteId);
    }

    @Override
    public List<SocDispatchEmpAddDO> selectDO(SocDispatchEmpAddQuery socDispatchEmpAddQuery, String siteId) {
        Example example = buildExample(socDispatchEmpAddQuery, siteId);
        return socDispatchEmpAddDAO.selectByExample(example);
    }

    @Override
    public List<SocDispatchEmpAddDO> listBySocDispatchId(String socDispatchId, String siteId) {
        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andEqualTo("socDispatchId", socDispatchId)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        example.orderBy("gmtCreate").desc();
        return socDispatchEmpAddDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int updateEmpAndItem(SocDispatchEmpAddAndItemUpdateDTO empAndItemUpdateDTO, TokenUser tokenUser) {
        SocDispatchEmpAddDO socDispatchEmpAddDO = selectDOById(empAndItemUpdateDTO.getId(), tokenUser.getSiteId());
        ParamException.notNull(socDispatchEmpAddDO, "要修改的员工增员参保记录不存在");

        SocDispatchVO socDispatch = socDispatchService.selectById(socDispatchEmpAddDO.getSocDispatchId(), tokenUser.getSiteId());
        ParamException.notNull(socDispatch, "员工社保派单记录不存在,无法进行修改");
        if (socDispatch.getHasSend()) {
            throw new ParamException("员工已被派单,请不要再进行修改");
        }

        // 分公司
        String childCompanyId = empAndItemUpdateDTO.getAssignCompanyId();
        ParamException.hasLength(childCompanyId, "请选择要分派的分公司");

        ChildCompanyDO childCompany = childCompanyService.selectDOById(childCompanyId, tokenUser.getSiteId());
        ParamException.notNull(childCompany, "选择的分公司不存在或已删除");
        // 分公司分管的区域
        List<GroupCityListVO> groupCityList = groupCityService.listByGroup(childCompany.getId(), tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(groupCityList)) {
            throw new ParamException("当前分公司没有分管区域,请选择其他分公司");
        }
        // 筛选拥有管理本用工单位权限的分公司
        boolean hasRole = groupCompanyService.groupHasRoleInCompany(childCompanyId, socDispatch.getCompanyId(), tokenUser.getSiteId());
        if (!hasRole) {
            throw new ParamException("选择的分公司不能分管当前用工单位,请检查");
        }

        // 参保方案
        SocProgDO socProg = null;
        if (StringUtils.isNotEmpty(socDispatchEmpAddDO.getSocProgId())) {
            socProg = socProgService.selectDOById(empAndItemUpdateDTO.getSocProgId(), tokenUser.getSiteId());
        }
        if (Objects.isNull(socProg)) {
            socProg = socProgService.findSocProgByName(empAndItemUpdateDTO.getSocProgName(), empAndItemUpdateDTO.getSocCityName(), tokenUser.getSiteId());
        }
        ParamException.notNull(socProg, "请选择参保方案");

        // 校验参保单位和分公司分管地区是否一致
        String socCityId = socProg.getSocCity();
        groupCityList = groupCityList.parallelStream()
                .filter(groupCity -> groupCity.getCityCode().equals(socCityId))
                .collect(Collectors.toList());
        ParamException.notEmpty(groupCityList, "参保方案所属地区不在所选分公司范围内,请修改");

        SocDispatchEmpAddUpdateDTO empAddUpdateDTO = new SocDispatchEmpAddUpdateDTO();
        empAddUpdateDTO.setId(empAndItemUpdateDTO.getId());

        // 参保方案,参保公司
        empAddUpdateDTO.setSocProgId(socProg.getId());
        empAddUpdateDTO.setSocProgName(socProg.getProgName());
        empAddUpdateDTO.setSocCityId(Long.valueOf(socProg.getSocCity()));
        empAddUpdateDTO.setSocCityName(socProg.getSocCityName());
        empAddUpdateDTO.setAssignCompanyName(childCompany.getName());
        empAddUpdateDTO.setAssignCompanyId(childCompany.getId());
        empAddUpdateDTO.setCompanyId(socDispatch.getCompanyId());
        empAddUpdateDTO.setNoError(true);
        empAddUpdateDTO.setSort(socDispatchEmpAddDO.getSort());
        // 用增员年月的当月第一天
        if (empAndItemUpdateDTO.getSocStartDate() != null) {
            empAddUpdateDTO.setSocStartDate(DateKit.getFirstDateOfMonth(empAndItemUpdateDTO.getSocStartDate()));
        } else {
            throw new ParamException("请传递社保参保时间");
        }
        if (empAndItemUpdateDTO.getFundStartDate() != null) {
            empAddUpdateDTO.setFundStartDate(DateKit.getFirstDateOfMonth(empAndItemUpdateDTO.getFundStartDate()));
        } else {
            throw new ParamException("请传递公积金参保时间");
        }
        // 存储分项信息
        updateEmpAddItem(socDispatchEmpAddDO.getSocDispatchId(), empAddUpdateDTO, socProg, empAndItemUpdateDTO.getItemUpdateList(), tokenUser);
        // 修改信息
        return update(empAddUpdateDTO, tokenUser);
    }

    /**
     * 员工参保分项信息
     *
     * @param socDispatchId   社保派单记录id
     * @param empAddUpdateDTO 员工参保记录
     * @param socProg         参保方案
     * @param itemUpdateList  分项信息<参保项,参保基数>
     * @param tokenUser       当前用户
     */
    private void updateEmpAddItem(String socDispatchId, SocDispatchEmpAddUpdateDTO empAddUpdateDTO, SocProgDO socProg, List<SocDispatchItemUpdateDTO> itemUpdateList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(itemUpdateList)) {
            throw new ParamException("员工没有具体的参保子项,请修改");
        }
        // 先删除旧的
        socDispatchEmpItemService.deleteBySocDispatchEmpAddId(empAddUpdateDTO.getId(), tokenUser);
        // 组织新的分项
        // 获取参保方案子项map,<子项名称,子项信息>map
        Map<String, SocProgItemDO> itemMap = getSocProgItemMap(socProg, tokenUser.getSiteId());

        List<SocDispatchEmpItemDO> itemAddList = new ArrayList<>();

        boolean noError = true;
        List<String> errorMsgList = new ArrayList<>();

        //个人社保缴费
        BigDecimal personalSocAmount = decimal0;
        //个人公积金缴费
        BigDecimal personalPfAmount = decimal0;
        //公司社保缴费
        BigDecimal companySocAmount = decimal0;
        //公司公积金缴费
        BigDecimal companyPfAmount = decimal0;

        for (SocDispatchItemUpdateDTO itemUpdateDTO : itemUpdateList) {
            String headName = itemUpdateDTO.getItemName();
            // 保险基数
            BigDecimal baseValue = itemUpdateDTO.getBaseAmount();
            baseValue = baseValue.setScale(2, BigDecimal.ROUND_HALF_UP);

            // 子项
            SocDispatchEmpItemDO itemAddDTO = new SocDispatchEmpItemDO();
            itemAddDTO.setSocDispatchId(socDispatchId);
            itemAddDTO.setEmpDispatchSocId(empAddUpdateDTO.getId());
            itemAddDTO.setSocProgId(socProg.getId());

            // 子项名称,参保基数
            itemAddDTO.setItemName(headName);
            itemAddDTO.setCardinalityPerson(baseValue);

            // 获取参保子项
            SocProgItemDO socProgItem = itemMap.get(headName);

            // 剔除已使用的,便于处理没有的项
            itemMap.remove(headName);

            if (Objects.isNull(socProgItem)) {
                // 没有对应参保项,基数还不是0
                if (baseValue.compareTo(new BigDecimal(0)) != 0) {
                    throw new ParamException("参保方案中没有参保项'" + headName + "',请不要设置此项的参保基数");
                }
                // 没有的参保项,默认0
                bulidEmpItemNoSocProgItem(headName, itemAddDTO);
            } else {
                // 校验
                if (baseValue.compareTo(socProgItem.getCardinalityMax()) == 1) {
                    // 大于最大范围
                    noError = false;
                    errorMsgList.add("参保项'" + headName + "'设置的参保基数大于参保方案参数范围");
                }
                if (baseValue.compareTo(socProgItem.getCardinalityMin()) == -1) {
                    // 小于参保范围
                    noError = false;
                    errorMsgList.add("参保项'" + headName + "'设置的参保基数小于参保方案参数范围");
                }
                // 根据设置的参保方案构建员工
                bulidEmpItemBySocProgItem(baseValue, socProg.getOnlySocAmount(), socProgItem, itemAddDTO);

                // 汇总到总的
                if (itemAddDTO.getItemType() == 1) {
                    // 社保
                    personalSocAmount = personalSocAmount.add(itemAddDTO.getAmountPersonal());
                    companySocAmount = companySocAmount.add(itemAddDTO.getAmountCompany());
                } else {
                    // 公积金
                    personalPfAmount = personalPfAmount.add(itemAddDTO.getAmountPersonal());
                    companyPfAmount = companyPfAmount.add(itemAddDTO.getAmountCompany());
                }
            }

            // 错误信息
            itemAddDTO.setNoError(true);
            itemAddDTO.setErrorMsg("");

            itemAddDTO.preInsert(tokenUser.getUserId());
            itemAddDTO.setSiteId(tokenUser.getSiteId());

            itemAddList.add(itemAddDTO);
        }
        // 尚有参保项没有处理
        if (itemMap.size() > 0) {
            for (String headName : itemMap.keySet()) {
                errorMsgList.add("参保项'" + headName + "'未设置参保基数");
            }
        }
        if (noError) {
            // 汇总数据
            empAddUpdateDTO.setPersonalSocAmount(personalSocAmount);
            empAddUpdateDTO.setPersonalPfAmount(personalPfAmount);
            empAddUpdateDTO.setCompanySocAmount(companySocAmount);
            empAddUpdateDTO.setCompanyPfAmount(companyPfAmount);

            //个人总缴费
            BigDecimal personalAmount = personalSocAmount.add(personalPfAmount);
            //公司总缴费
            BigDecimal companyAmount = companySocAmount.add(companyPfAmount);
            //公司总缴费
            BigDecimal allAmount = companyAmount.add(personalAmount);

            empAddUpdateDTO.setPersonalAmount(personalAmount);
            empAddUpdateDTO.setCompanyAmount(companyAmount);
            empAddUpdateDTO.setAllAmount(allAmount);

            empAddUpdateDTO.setNoError(true);
            // 批量保存
            socDispatchEmpItemService.batchSaveDoList(itemAddList, tokenUser);
        } else {
            String errorMsg = String.join(",", errorMsgList);
            throw new ParamException(errorMsg);
        }
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void deleteBySocDispatchId(String siteId, String... socDispatchIds) {
        if (socDispatchIds == null || socDispatchIds.length == 0) {
            return;
        }
        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andIn("socDispatchId", Arrays.asList(socDispatchIds))
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);

        // 导入记录对应的员工
        List<SocDispatchEmpAddDO> dispatchEmpAddList = socDispatchEmpAddDAO.selectByExample(example);
        if (!CollectionUtils.isEmpty(dispatchEmpAddList)) {
            String[] dispatchEmpAddIdList = dispatchEmpAddList.parallelStream()
                    .map(SocDispatchEmpAddDO::getId)
                    .toArray(String[]::new);

            // 删除分项
            socDispatchEmpItemService.deleteBySocDispatchEmpAddIds(siteId, dispatchEmpAddIdList);

            // 删除员工记录
            socDispatchEmpAddDAO.deleteByExample(example);

            // 删除参保记录
            empSocDispatchService.deleteBySocDispatchEmpAddIds(siteId, dispatchEmpAddIdList);
        }
    }

    @Override
    public List<SocDispatchEmpAddDO> listBySocDispatchIds(String siteId, String... socDispatchIds) {
        if (socDispatchIds == null || socDispatchIds.length == 0) {
            return null;
        }
        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andIn("socDispatchId", Arrays.asList(socDispatchIds))
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);

        // 导入记录对应的员工
        return socDispatchEmpAddDAO.selectByExample(example);
    }

    @Override
    public List<SocDispatchEmpAddDO> listByIds(String siteId, String... ids) {
        if (ids == null || ids.length == 0) {
            return null;
        }
        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andIn("id", Arrays.asList(ids))
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);

        // 导入记录对应的员工
        return socDispatchEmpAddDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void sendBySocDispatchId(String socDispatchId, String siteId) {
        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andEqualTo("socDispatchId", socDispatchId)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);

        SocDispatchEmpAddDO updateEmp = new SocDispatchEmpAddDO();
        updateEmp.setHasSend(true);

        socDispatchEmpAddDAO.updateByExampleSelective(updateEmp, example);
    }

    /**
     * 根据查询参数，构建example
     *
     * @param socDispatchEmpAddQuery 查询参数
     * @param siteId                 所属站点id
     * @return example
     */
    private Example buildExample(SocDispatchEmpAddQuery socDispatchEmpAddQuery, String siteId) {
        Example example = new Example(SocDispatchEmpAddDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (socDispatchEmpAddQuery.getSocDispatchId() != null) {
            example.and().andEqualTo("socDispatchId", socDispatchEmpAddQuery.getSocDispatchId());
        }
        if (socDispatchEmpAddQuery.getYear() != null) {
            example.and().andEqualTo("year", socDispatchEmpAddQuery.getYear());
        }
        if (socDispatchEmpAddQuery.getMonth() != null) {
            example.and().andEqualTo("month", socDispatchEmpAddQuery.getMonth());
        }
        if (socDispatchEmpAddQuery.getName() != null) {
            example.and().andLike("name", "%" + socDispatchEmpAddQuery.getName() + "%");
        }
        if (socDispatchEmpAddQuery.getPhone() != null) {
            example.and().andEqualTo("phone", socDispatchEmpAddQuery.getPhone());
        }
        if (socDispatchEmpAddQuery.getIdCard() != null) {
            example.and().andEqualTo("idCard", socDispatchEmpAddQuery.getIdCard());
        }
        if (socDispatchEmpAddQuery.getSocProgName() != null) {
            example.and().andEqualTo("socProgName", socDispatchEmpAddQuery.getSocProgName());
        }
        if (socDispatchEmpAddQuery.getSocProgId() != null) {
            example.and().andEqualTo("socProgId", socDispatchEmpAddQuery.getSocProgId());
        }
        if (socDispatchEmpAddQuery.getSocCityName() != null) {
            example.and().andEqualTo("socCityName", socDispatchEmpAddQuery.getSocCityName());
        }
        if (socDispatchEmpAddQuery.getSocCityId() != null) {
            example.and().andEqualTo("socCityId", socDispatchEmpAddQuery.getSocCityId());
        }
        if (socDispatchEmpAddQuery.getSocStartDateStart() != null &&
                socDispatchEmpAddQuery.getSocStartDateEnd() != null) {
            example.and().andBetween("socStartDate", socDispatchEmpAddQuery.getSocStartDateStart(),
                    socDispatchEmpAddQuery.getSocStartDateEnd());
        }
        if (socDispatchEmpAddQuery.getFundStartDateStart() != null &&
                socDispatchEmpAddQuery.getFundStartDateEnd() != null) {
            example.and().andBetween("fundStartDate", socDispatchEmpAddQuery.getFundStartDateStart(),
                    socDispatchEmpAddQuery.getFundStartDateEnd());
        }
        if (socDispatchEmpAddQuery.getIsShift() != null) {
            example.and().andEqualTo("isShift", socDispatchEmpAddQuery.getIsShift());
        }
        if (socDispatchEmpAddQuery.getAssignCompanyId() != null) {
            example.and().andEqualTo("assignCompanyId", socDispatchEmpAddQuery.getAssignCompanyId());
        }
        if (socDispatchEmpAddQuery.getAssignCompanyName() != null) {
            example.and().andEqualTo("assignCompanyName", socDispatchEmpAddQuery.getAssignCompanyName());
        }
        if (socDispatchEmpAddQuery.getRemake() != null) {
            example.and().andEqualTo("remake", socDispatchEmpAddQuery.getRemake());
        }
        if (socDispatchEmpAddQuery.getHasSend() != null) {
            example.and().andEqualTo("hasSend", socDispatchEmpAddQuery.getHasSend());
        }
        if (socDispatchEmpAddQuery.getHasReceive() != null) {
            example.and().andEqualTo("hasReceive", socDispatchEmpAddQuery.getHasReceive());
        }
        if (socDispatchEmpAddQuery.getStatus() != null) {
            example.and().andEqualTo("status", socDispatchEmpAddQuery.getStatus());
        }
        if (socDispatchEmpAddQuery.getPersonalSocAmount() != null) {
            example.and().andEqualTo("personalSocAmount", socDispatchEmpAddQuery.getPersonalSocAmount());
        }
        if (socDispatchEmpAddQuery.getPersonalPfAmount() != null) {
            example.and().andEqualTo("personalPfAmount", socDispatchEmpAddQuery.getPersonalPfAmount());
        }
        if (socDispatchEmpAddQuery.getPersonalAmount() != null) {
            example.and().andEqualTo("personalAmount", socDispatchEmpAddQuery.getPersonalAmount());
        }
        if (socDispatchEmpAddQuery.getCompanySocAmount() != null) {
            example.and().andEqualTo("companySocAmount", socDispatchEmpAddQuery.getCompanySocAmount());
        }
        if (socDispatchEmpAddQuery.getCompanyPfAmount() != null) {
            example.and().andEqualTo("companyPfAmount", socDispatchEmpAddQuery.getCompanyPfAmount());
        }
        if (socDispatchEmpAddQuery.getCompanyAmount() != null) {
            example.and().andEqualTo("companyAmount", socDispatchEmpAddQuery.getCompanyAmount());
        }
        if (socDispatchEmpAddQuery.getAllAmount() != null) {
            example.and().andEqualTo("allAmount", socDispatchEmpAddQuery.getAllAmount());
        }
        if (socDispatchEmpAddQuery.getNoError() != null) {
            example.and().andEqualTo("noError", socDispatchEmpAddQuery.getNoError());
        }
        if (socDispatchEmpAddQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", socDispatchEmpAddQuery.getCompanyId());
        }
        if (socDispatchEmpAddQuery.getSort() != null) {
            example.and().andEqualTo("sort", socDispatchEmpAddQuery.getSort());
        }
        example.orderBy("sort").asc();
//        // 排序
//        ExampleKit.setExampleOrder(example,socDispatchEmpLessQuery.getOrders());
        return example;
    }
}




